import type {
  HttpError,
  HttpRequestConfig,
  HttpResponse,
} from 'uview-plus/libs/luch-request/index';
import { showMessage } from './status';
import { getTerminal, tenantId } from './untils';
import { getRefreshToken, getToken, setToken } from '@/utils/auth';
import storage from '@/utils/storage';
import useUserStore from '@/store/modules/user';
import { useModal } from '@/hooks';
import { refreshToken as refreshTokenApi } from '@/api/user';

// // 是否正在刷新token的标记
// const isRefreshing: boolean = false;
// // 重试队列，每一项将是一个待执行的函数形式
// const requestQueue: (() => void)[] = [];

function requestInterceptors() {
  /**
   * 请求拦截
   * @param {object} http
   */
  uni.$u.http.interceptors.request.use(
    (config: HttpRequestConfig) => {
      // 可使用async await 做异步操作
      // 初始化请求拦截器时，会执行此方法，此时data为undefined，赋予默认{}
      config.data = config.data || {};
      if (config.header) {
        config.header.terminal = getTerminal();
        config.header['tenant-id'] = tenantId;
        // config.header.platform = 'WechatMiniProgram';
      }

      // 是否需要设置 token
      const isToken = config.custom?.auth === false;
      // 是否需要防止数据重复提交
      const isRepeatSubmit = config.custom?.repeatSubmit === false;
      if (getToken() && !isToken && config.header) {
        // token设置
        config.header.Authorization = getToken();
      }

      if (!isRepeatSubmit && (config.method === 'POST' || config.method === 'UPLOAD')) {
        const requestObj = {
          url: config.url,
          data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
          time: new Date().getTime(),
        };
        const sessionObj = storage.getJSON('sessionObj');
        if (!sessionObj) {
          storage.setJSON('sessionObj', requestObj);
        }
        else {
          const s_url = sessionObj.url; // 请求地址
          const s_data = sessionObj.data; // 请求数据
          const s_time = sessionObj.time; // 请求时间
          const interval = 1000; // 间隔时间(ms)，小于此时间视为重复提交
          if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
            const message = '数据正在处理，请勿重复提交';
            console.warn(`[${s_url}]: ${message}`);
            return Promise.reject(new Error(message));
          }
          else {
            storage.setJSON('sessionObj', requestObj);
          }
        }
      }
      return config;
    },
    (
      config: any, // 可使用async await 做异步操作
    ) => Promise.reject(config),
  );
}

/**
 * 处理 401 未登录的错误
 */
const handleAuthorized = () => {
  const userStore = useUserStore();
  const { showAuthModal } = useModal();
  userStore.logout();
  showAuthModal();
  // 登录超时
  return Promise.reject(new Error('请登录'));
};

// Axios 无感知刷新令牌，参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
let requestList: (() => void)[] = []; // 请求队列
let isRefreshToken = false; // 是否正在刷新中
const refreshToken = async (config: HttpRequestConfig) => {
  // 如果当前已经是 refresh-token 的 URL 地址，并且还是 401 错误，说明是刷新令牌失败了，直接返回 Promise.reject(error)
  if (config.url?.includes('/member/auth/refresh-token')) {
    return Promise.reject(new Error('Token refresh failed'));
  }

  // 如果未认证，并且未进行刷新令牌，说明可能是访问令牌过期了
  if (!isRefreshToken) {
    isRefreshToken = true;
    // 1. 如果获取不到刷新令牌，则只能执行登出操作
    const token = getRefreshToken();
    if (!token) {
      return handleAuthorized();
    }
    // 2. 进行刷新访问令牌
    try {
      const refreshTokenResult = await refreshTokenApi({ refreshToken: token });
      console.log('refreshTokenResult=====', refreshTokenResult);
      if (!refreshTokenResult) {
        // 如果刷新不成功，直接抛出 e 触发 2.2 的逻辑
        // noinspection ExceptionCaughtLocallyJS
        throw new Error('刷新令牌失败');
      }
      // 2.1 刷新成功，则回放队列的请求 + 当前请求
      if (getToken() && config.header) {
        // token设置
        config.header.Authorization = getToken();
      }
      requestList.forEach((cb) => {
        cb();
      });
      requestList = [];
      return uni.$u.http.request(config);
    }
    catch (e) {
      // 为什么需要 catch 异常呢？刷新失败时，请求因为 Promise.reject 触发异常。
      // 2.2 刷新失败，只回放队列的请求
      requestList.forEach((cb) => {
        cb();
      });
      // 提示是否要登出。即不回放当前请求！不然会形成递归
      return handleAuthorized();
    }
    finally {
      requestList = [];
      isRefreshToken = false;
    }
  }
  else {
    // 添加到队列，等待刷新获取到新的令牌
    return new Promise((resolve) => {
      requestList.push(() => {
        if (getToken() && config.header) {
          // token设置
          config.header.Authorization = getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
        }
        resolve(uni.$u.http.request(config));
      });
    });
  }
};

function responseInterceptors() {
  /**
   * 响应拦截
   * @param {object} http
   */
  uni.$u.http.interceptors.response.use(
    async (response: HttpResponse) => {
      /* 对响应成功做点什么 可使用async await 做异步操作 */
      const data = response.data;
      // 配置参数
      const config = response.config;
      // 自定义参数
      const custom = config?.custom;

      // 约定：如果是 /auth/ 下的 URL 地址，并且返回了 accessToken 说明是登录相关的接口，则自动设置登陆令牌
      if (config?.url?.includes('/member/auth/') && response.data?.data?.accessToken) {
        setToken(response.data.data.accessToken, response.data.data.refreshToken);
      }

      // 请求成功则返回结果
      if (data.code === 0)
        return data || {};

      // 特殊：如果 401 错误码，则跳转到登录页 or 刷新令牌
      if (data.code === 401) {
        return refreshToken(response.config);
      }

      if (data.code === 403) {
        return {};
      }

      // 登录状态失效，重新登录
      // if (data.code === 401) {
      //   // 是否在获取token中,防止重复获取
      //   if (!isRefreshing) {
      //     // 修改登录状态为true
      //     isRefreshing = true;
      //     await useUserStore().authLogin();
      //     // 登录完成之后，开始执行队列请求
      //     requestQueue.forEach(cb => cb());
      //     // 重试完了清空这个队列
      //     requestQueue = [];
      //     isRefreshing = false;
      //     // 重新执行本次请求
      //     return uni.$u.http.request(config);
      //   }
      //   else {
      //     return new Promise((resolve) => {
      //       // 将resolve放进队列，用一个函数形式来保存，等登录后直接执行
      //       requestQueue.push(() => {
      //         resolve(uni.$u.http.request(config));
      //       });
      //     });
      //   }
      // }

      // 如果没有显式定义custom的toast参数为false的话，默认对报错进行toast弹出提示
      if (custom?.toast !== false)
        uni.$u.toast(data.message);

      // 如果需要catch返回，则进行reject
      if (custom?.catch) {
        return Promise.reject(data);
      }
      else {
        // 否则返回一个pending中的promise
        return new Promise(() => {});
      }
    },
    (response: HttpError) => {
      if (response.statusCode) {
        // 请求已发出，但是不在2xx的范围
        showMessage(response.statusCode);
        return Promise.reject(response.data);
      }
      showMessage('网络连接异常,请稍后再试!');
    },
  );
}

export { requestInterceptors, responseInterceptors };
